/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands.connection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;

import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;

/**
 * Light Command used by real Command.
 * This command move all the anchors attached to the provided Node or Edge. It also
 * move the edges attached to the anchors, and the anchors attached to these edges and so on.
 * 
 * @author dumoulin
 */
public class MoveRectangleAnchorsCommand {

	/**
	 * The Element owning the anchors : GraphNode or GraphEdge.
	 */
	GraphElement node;
	
	/**
	 * List of individual commands to move the anchors.
	 */
	List<MoveAnchorPositionAndAttachedEdgesCommand> moveAnchorCommand;

	/**
	 * Constructor.
	 * 
	 * @param node The node to which the anchors should be moved.
	 */
	public MoveRectangleAnchorsCommand(GraphElement node) {
		this.node = node;
	}
	
	/**
	 * save the positions of the node.
	 */
	public void savePositions()
	{
		moveAnchorCommand = new ArrayList<MoveAnchorPositionAndAttachedEdgesCommand>();

		Iterator<GraphConnector> iter = getAnchor().iterator();
		while(iter.hasNext())
		{
			GraphConnector anchor = iter.next();
			MoveAnchorPositionAndAttachedEdgesCommand cmd = new MoveAnchorPositionAndAttachedEdgesCommand(anchor);
			moveAnchorCommand.add(cmd);
			cmd.savePositions();
		}
	}


	/**
	 * Restore the positions of the node.
	 */
	public void restorePositions()
	{
		if(moveAnchorCommand==null)
			return;

		// Check lists size
		assert(moveAnchorCommand.size()==getAnchor().size());

		Iterator<MoveAnchorPositionAndAttachedEdgesCommand> saved = moveAnchorCommand.iterator();
		while(saved.hasNext())
		{
			MoveAnchorPositionAndAttachedEdgesCommand cmd = saved.next();
			cmd.restorePositions();
		}
	}

	/**
	 * Move each attached anchor.
	 * 
	 * @param newBounds 
	 * @param oldBounds 
	 */
	public void moveAttachedAnchors(Rectangle newBounds, Rectangle oldBounds)
	{
		Iterator<MoveAnchorPositionAndAttachedEdgesCommand> saved = moveAnchorCommand.iterator();
		while(saved.hasNext())
		{
			MoveAnchorPositionAndAttachedEdgesCommand cmd = saved.next();
			cmd.moveAnchor( computeAnchorPosition(cmd.getOldPosition().getCopy(), newBounds, oldBounds) );
		}
	}

	/**
	 * Recompute the position according to the new Constraints.
	 * Modify the provided Point location.
	 * 
	 * @param newBounds New bounds of the owning node
	 * @param oldBounds Old bounds of the owning node
	 * @param oldPos The oldPos. This object is modified.
	 * 
	 * @return The new pos. Return the oldPas Object.
	 */
	private Point computeAnchorPosition(Point oldPos, Rectangle newBounds, Rectangle oldBounds) {
		int resizeNorth=0;
		int resizeSouth=0;
		int resizeEast=0;
		int resizeWest=0;

		Point temporyLocation=oldPos.getCopy();
		Point oldLocation = oldBounds.getLocation();
		Point newLocation = newBounds.getLocation();

		// resize
		if( newBounds.width!=oldBounds.width|| newBounds.height!=oldBounds.height){
			if( newBounds.y!=oldBounds.y){
				resizeNorth=oldBounds.y-newBounds.y;
			}
			if( newBounds.x!=oldBounds.x){
				resizeWest=oldBounds.x-newBounds.x;
			}
			if( newBounds.x!=oldBounds.x){
				resizeWest=oldBounds.x-newBounds.x;
			}
			if( newBounds.x==oldBounds.x&& newBounds.width!=oldBounds.width){
				resizeEast=oldBounds.width-newBounds.width;
			}
			if( newBounds.y==oldBounds.y&& newBounds.height!=oldBounds.height){
				resizeSouth=oldBounds.height-newBounds.height;
			}

			//position of the anchor
			Rectangle smallRectangle=oldBounds.getCopy();
			smallRectangle.crop(new Insets(5));

			//north
			if(smallRectangle.getPosition(temporyLocation)==PositionConstants.NORTH
					||smallRectangle.getPosition(temporyLocation)==PositionConstants.NORTH_EAST
					||smallRectangle.getPosition(temporyLocation)==PositionConstants.NORTH_WEST){
				temporyLocation.y-=resizeNorth;
				//case whe the pos is not in the north segment
				if(temporyLocation.x<newBounds.x){
					temporyLocation.x=newBounds.x;
				}
				if(temporyLocation.x>newBounds.x+newBounds.width){
					temporyLocation.x=newBounds.x+newBounds.width;
				}
			}
			//sud
			if(smallRectangle.getPosition(temporyLocation)==PositionConstants.SOUTH
					||smallRectangle.getPosition(temporyLocation)==PositionConstants.SOUTH_EAST
					||smallRectangle.getPosition(temporyLocation)==PositionConstants.SOUTH_WEST){
				temporyLocation.y-=resizeSouth;
//				case whe the pos is not in the south segment
				if(temporyLocation.x<newBounds.x){
					temporyLocation.x=newBounds.x;
				}
				if(temporyLocation.x>newBounds.x+newBounds.width){
					temporyLocation.x=newBounds.x+newBounds.width;
				}
			}
			//East
			if(smallRectangle.getPosition(temporyLocation)==PositionConstants.EAST){
				temporyLocation.x-=resizeEast;
//				case whe the pos is not in the EAST segment
				if(temporyLocation.y<newBounds.y){
					temporyLocation.y=newBounds.y;
				}
				if(temporyLocation.y>newBounds.y+newBounds.height){
					temporyLocation.y=newBounds.y+newBounds.height;
				}
			}
			//weast
			if(smallRectangle.getPosition(temporyLocation)==PositionConstants.WEST){
				temporyLocation.x-=resizeWest;
//				case whe the pos is not in the west segment
				if(temporyLocation.y<newBounds.y){
					temporyLocation.y=newBounds.y;
				}
				if(temporyLocation.y>newBounds.y+newBounds.height){
					temporyLocation.y=newBounds.y+newBounds.height;}
			}

		}
		else{
			//it may be a move and a resize but in this case we take in account the position
			int dx = newLocation.x - oldLocation.x;
			int dy = newLocation.y - oldLocation.y;

			temporyLocation.x +=dx;
			temporyLocation.y +=dy;
		}
		oldPos.x=temporyLocation.x;
		oldPos.y=temporyLocation.y;
		return oldPos;
	}

	/**
	 * Get the list of anchors of the node.
	 * 
	 * @return 
	 */
	public List getAnchor()
	{
		return node.getAnchorage();
	}


} // end inner class